package com.zanglikun.framework.utils;

import com.zanglikun.framework.annoation.Controller;
import com.zanglikun.framework.annoation.RequestMapping;
import com.zanglikun.framework.model.UrlMapping;
import javassist.*;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;
import org.reflections.Reflections;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

/**
 * @author : zanglikun
 * @date : 2021/2/23 13:27
 * @Version: 1.0
 * @Desc : ClassUtils 扫描我们自定义注解的类 和 方法
 */
public class ClassUtils {

    // 创建 收集数据的对象
    private static Map<String, UrlMapping> map = new HashMap<>();


    /** 此方法 用于扫描被 Controller 和 RequestMapping 类和方法的 集合，将来可以用来判断 */
    public static Map<String, UrlMapping> getUrlMappings(String basePackageName) throws IllegalAccessException, InstantiationException, NotFoundException {

        // 创建反射对象，
        Reflections reflections = new Reflections(basePackageName);

        // 获取 被 @Controller 修饰的对象的字节码信息
        Set<Class<?>> classes = reflections.getTypesAnnotatedWith(Controller.class);

        // 遍历 这些 类的字节码信息
        for (Class<?> aClass : classes) {
            // 判断 类上有 @RequestMapping 注解的 类
            RequestMapping classAnnotation = aClass.getDeclaredAnnotation(RequestMapping.class);
            // 定义 类上的 注解 值
            String baseUri = "";
            // 判断 是否有这个对象
            if (classAnnotation != null) {
                // 如果有 获取 类的注解名
                String classAnnotationname = classAnnotation.value();
                // baseUri 拼接上类的注解名
                baseUri += classAnnotationname;
            }
            Object obj = aClass.newInstance();

            // 拿到这些类，我们就获取其中所有的方法信息
            Method[] methods = aClass.getMethods();

            // 遍历 这些方法
            for (Method method : methods) {
                //判断 注解 其中被 @RequestMapping 修饰的方法
                RequestMapping methodAnnotation = method.getDeclaredAnnotation(RequestMapping.class);
                // 如果有被标注的方法
                if (methodAnnotation != null) {
                    // 定义 方法上的 注解值
                    String value = methodAnnotation.value();
                    // 获取 新对象信息 即 方法参数 名字
                    ClassPool classPool = ClassPool.getDefault();
                    classPool.insertClassPath(new ClassClassPath(aClass));
                    CtMethod cm = classPool.getMethod(aClass.getName(), method.getName());
                    MethodInfo methodInfo = cm.getMethodInfo();
                    CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
                    LocalVariableAttribute attribute =
                            (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);

                    // 我们需要定义一个集合，收取 参数名称、类型 等信息因为参数有顺序，我们K 放置 参数名称，Value 放置参数类型
                    Map<String,Class<?>> parameters = new LinkedHashMap<>();
                    Class<?>[] classTYPE = method.getParameterTypes();
                    if (attribute != null) {
                        int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
                        for (int i = 0; i < method.getParameterCount(); i++) {
                            String argName = attribute.variableName(i + pos);
                            parameters.put(argName,classTYPE[i]);
                        }
                    }
                    map.put(baseUri + value, new UrlMapping(obj, method,parameters));
                }
            }
        }
        return map;
    }

}

